package com.spa.infrastructure.service.wx;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.SignUtil;
import cn.hutool.crypto.digest.DigestAlgorithm;
import cn.hutool.json.JSONUtil;
import com.spa.application.dto.wx.WXConfigDTO;
import com.spa.infrastructure.util.StringUtils;
import com.spa.infrastructure.util.UUID;
import com.spa.infrastructure.enums.dict.base.SourceTypeEnum;
import com.spa.infrastructure.exception.SpaException;
import com.spa.infrastructure.service.wx.config.ChatConfig;
import com.spa.infrastructure.service.wx.result.ChatUserInfoResult;
import com.spa.infrastructure.service.wx.result.jssdk.AccessTokenResult;
import com.spa.infrastructure.service.wx.result.jssdk.TicketResult;
import com.spa.infrastructure.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class WXJsSdkService {

    private static final String ACCESS_TOKEN = "JS_SDK_ACCESS_TOKEN";
    private static final String TICKET = "JS_API_TICKET";

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private ChatConfig chatConfig;

    /**
     * 获取初始化签名
     *
     * @param url 要签名的url
     * @return config对象
     */
    public WXConfigDTO sign(String url) {
        String noncestr = UUID.fastUUID().toString(true);
        String jsapi_ticket = getTicket();
        long timestamp = DateUtil.currentSeconds();
        Map<String, Object> map = new HashMap<>();
        map.put("jsapi_ticket", jsapi_ticket);
        map.put("noncestr", noncestr);
        map.put("timestamp", timestamp);
        map.put("url", url);
        log.info("{}  {}  {}  {}", jsapi_ticket, noncestr, timestamp, url);
        String sign = SignUtil.signParams(DigestAlgorithm.SHA1, map, "&", "=", true);
        WXConfigDTO wxConfigDTO = new WXConfigDTO();
        wxConfigDTO.setAppId(chatConfig.getAppid());
        wxConfigDTO.setNonceStr(noncestr);
        wxConfigDTO.setTimestamp(timestamp);
        wxConfigDTO.setSignature(sign);
        return wxConfigDTO;
    }

    /**
     * 通过指定的APPID获取ticket
     *
     * @return ticket
     */
    public String getTicket() {
        String ticket = redisUtil.get(TICKET);
        if (StringUtils.isNotEmpty(ticket)) {
            return ticket;
        }
        String accessToken = getAccessToken(SourceTypeEnum.WeChat);
        TicketResult ticketResult = getTicketFromWX(accessToken);
        if (ticketResult.getErrcode() != 0) {
            throw new SpaException(ticketResult.getErrmsg());
        }
        ticket = ticketResult.getTicket();
        long timeout = ticketResult.getExpires_in();
        redisUtil.set(TICKET, ticket, timeout - 60);
        return ticket;
    }

    /**
     * 通过指定的APPID获取小程序 或公众号的accessToken
     *
     * @return accessToken
     */
    public String getAccessToken(SourceTypeEnum sourceTypeEnum) {
        String rediskey = sourceTypeEnum.getCode() + "-" + ACCESS_TOKEN;
        String accessToken = redisUtil.get(rediskey);
        if (StringUtils.isNotEmpty(accessToken)) {
            //TODO 验证token是否有效
            return accessToken;
        }
        AccessTokenResult accessTokenResult = getAccessTokenFromWX();
        if (ObjectUtil.isNotNull(accessTokenResult.getErrcode())) {
            throw new SpaException(accessTokenResult.getErrmsg());
        }
        accessToken = accessTokenResult.getAccess_token();
        long timeout = accessTokenResult.getExpires_in();
        redisUtil.set(rediskey, accessToken, timeout - 60);
        return accessToken;
    }

    /**
     * 请求微信服务器获取 Access token
     *
     * @return access_token
     */
    public AccessTokenResult getAccessTokenFromWX() {
        Map<String, Object> params = new HashMap<>();
        params.put("grant_type", "client_credential");
        params.put("force_refresh", false);
        params.put("appid", chatConfig.getAppid());
        params.put("secret", chatConfig.getSecret());
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(
                Constant.TOKEN, params, String.class);
        return JSONUtil.toBean(responseEntity.getBody(), AccessTokenResult.class);
    }

    /**
     * 通过 accessToken 获取ticket
     *
     * @param accessToken token
     * @return 获取ticket
     */
    private TicketResult getTicketFromWX(String accessToken) {
        String url = Constant.TICKET.replace("ACCESS_TOKEN", accessToken);
        String data = restTemplate.getForObject(url, String.class);
        return JSONUtil.toBean(data, TicketResult.class);
    }


    /**
     * 查询公众号用户信息
     *
     * @param openId
     * @return
     */
    public ChatUserInfoResult getUserInfo(SourceTypeEnum source, String openId) {
        Map<String, String> params = new HashMap<>();
        params.put("TOKEN", getAccessToken(source));
        params.put("OPENID", openId);
        log.info("getUserInfo-params : {}", params);
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(Constant.WECHAT_USERINFO, String.class, params);
        log.info("getUserInfo-responseEntity : {}", responseEntity);
        return JSONUtil.toBean(responseEntity.getBody(), ChatUserInfoResult.class);
    }

}
